﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Net.NetworkInformation;
using System.Net.Sockets;
using System.Reflection;
using System.Security.Cryptography;
using System.Text;
using System.Threading;
using System.Xml.Serialization;

using vJine.Core.Base;

namespace vJine.Core {

    //Base
    public partial class Utility {
        public static string getVersion<T>() where T : class, new() {
            string FullName = typeof(T).AssemblyQualifiedName;

            int index_v = FullName.IndexOf("Version=") + 8;
            int index_V = FullName.IndexOf(',', index_v);

            string version = FullName.Substring(index_v, index_V - index_v);

            return version;
        }

        public static string UrlEncode(string V) {
            return System.Web.HttpUtility.UrlEncode(V, Encoding.UTF8);
        }

        public static string UrlDecode(string V) {
            return System.Web.HttpUtility.UrlDecode(V, Encoding.UTF8);
        }

        public static string UrlPathEncode(string url_path) {
            return System.Web.HttpUtility.UrlPathEncode(url_path);
        }

        public static string BytesToHex(byte[] Bytes) {
            StringBuilder Result = new StringBuilder();
            string HexAlphabet = "0123456789ABCDEF";

            foreach (byte B in Bytes) {
                Result.Append(HexAlphabet[(int)(B >> 4)]);
                Result.Append(HexAlphabet[(int)(B & 0xF)]);
            }

            return Result.ToString();
        }

        public static byte[] HexToBytes(string Hex) {

            byte[] Bytes = new byte[Hex.Length / 2];
            int[] HexValue =
                    new int[] { 0x00, 0x01, 0x02, 0x03,0x04, 0x05,0x06, 0x07,0x08, 0x09,
                                0x00, 0x00,0x00, 0x00, 0x00, 0x00,0x00,
                                0x0A, 0x0B, 0x0C,0x0D, 0x0E, 0x0F };

            for (int x = 0, i = 0; i < Hex.Length; i += 2, x += 1) {
                Bytes[x] = (byte)(HexValue[Char.ToUpper(Hex[i + 0]) - '0'] << 4 |
                                  HexValue[Char.ToUpper(Hex[i + 1]) - '0']);
            }

            return Bytes;
        }

        public static T[] ToSubArray<T>(T[] V, int start, int length) {
            if (object.Equals(V, null)) {
                return null;
            }

            if (length == V.Length) {
                return V;
            }

            T[] X = new T[length];
            Array.Copy(V, start, X, 0, length);
            return X;
        }

        public string Format(string format, params object[] Params) {
            return string.Format(format, Params);
        }
    }

    //Stream
    public partial class Utility {
        public static byte[] Read(Stream Stream, int maxLen, params byte[][] endings) {
            int matchedEnding = 0;
            return Read(Stream, maxLen, ref matchedEnding, endings);
        }

        public static byte[] Read(Stream Stream, int maxLen, ref int matchedEnding, params byte[][] endings) {
            return Read(Stream, maxLen, false, ref matchedEnding, endings);
        }

        public static byte[] Read(Stream Stream, int maxLen, bool ReturnIfHitsMax, ref int matchedEnding, params byte[][] endings) {
            MemoryStream mm = new MemoryStream();
            Read(Stream, mm, maxLen, ReturnIfHitsMax, ref matchedEnding, endings);
            return mm.ToArray();
        }

        public static void Read(Stream read_stream, Stream write_stream, int maxLen, bool ReturnIfHitsMax, ref int matchedEnding, params byte[][] endings) {
            matchedEnding = -1;
            int endingQty = endings.Length;
            int[] matchCounter = new int[endingQty];
            int[] remainCounter = new int[endingQty];

            int length2read = endings[0].Length;
            int bufferMax = endings[0].Length;
            for (int i = 0; i < matchCounter.Length; i++) {
                remainCounter[i] = endings[i].Length;
                if (length2read > endings[i].Length) {
                    length2read = endings[i].Length;
                }
                if (bufferMax < endings[i].Length) {
                    bufferMax = endings[i].Length;
                }
            }

            int len_read = 0, len_read_total = 0;
            byte[] B = new byte[bufferMax];
            do {
                len_read = Utility.Read(read_stream, B, length2read);
                len_read_total += len_read;
                write_stream.Write(B, 0, len_read);

                for (int i = 0; i < len_read; i++) {
                    byte b = B[i];

                    for (int j = 0; j < endingQty; j++) {
                        int k = matchCounter[j];
                        if (b == endings[j][k]) {
                            matchCounter[j] += 1;
                            remainCounter[j] -= 1;
                        } else {
                            matchCounter[j] = 0;
                            remainCounter[j] = endings[j].Length;
                        }
                    }
                }

                length2read =
                    ReturnIfHitsMax ? (int)Math.Min(endings[0].Length, maxLen - len_read_total) : endings[0].Length;
                for (int i = 0; i < endingQty; i++) {
                    if (length2read > remainCounter[i]) {
                        length2read = remainCounter[i];
                    }
                    if (length2read == 0) {
                        matchedEnding = i;
                        break;
                    }
                }
                if(ReturnIfHitsMax && len_read_total >= maxLen) {
                    return;
                }
            } while (length2read > 0);
        }

        public static int Read(Stream read_stream, byte[] rBuffer, ref int matchedEnding, int max, params byte[][] endings) {
            matchedEnding = -1;
            int maxLen =
                max > 0 ? Math.Min(max, rBuffer.Length) : rBuffer.Length;

            int endingQty = endings.Length;
            int[] matchCounter = new int[endingQty];
            int[] remainCounter = new int[endingQty];

            int length2read = endings[0].Length;
            int bufferMax = endings[0].Length;
            for(int i = 0; i < matchCounter.Length; i++) {
                remainCounter[i] = endings[i].Length;
                if(length2read > endings[i].Length) {
                    length2read = endings[i].Length;
                }
                if(bufferMax < endings[i].Length) {
                    bufferMax = endings[i].Length;
                }
            }

            int len_read = 0, len_read_total = 0;
            byte[] B = new byte[bufferMax];
            do {
                len_read = Utility.Read(read_stream, B, length2read);
                Array.Copy(B, 0, rBuffer, len_read_total, len_read);
                len_read_total += len_read;

                for(int i = 0; i < len_read; i++) {
                    byte b = B[i];

                    for(int j = 0; j < endingQty; j++) {
                        int k = matchCounter[j];
                        if(b == endings[j][k]) {
                            matchCounter[j] += 1;
                            remainCounter[j] -= 1;
                        } else {
                            matchCounter[j] = 0;
                            remainCounter[j] = endings[j].Length;
                        }
                    }
                }

                length2read =
                    (int)Math.Min(endings[0].Length, maxLen - len_read_total);
                for(int i = 0; i < endingQty; i++) {
                    if(length2read > remainCounter[i]) {
                        length2read = remainCounter[i];
                    }
                    if(length2read == 0) {
                        matchedEnding = i;
                        break;
                    }
                }
            } while(length2read > 0);

            return len_read_total;
        }

        public static void Write(Stream Stream, char V) {
            Write(Stream, Encoding.UTF8.GetBytes(new char[] { V }));
        }

        public static void Write(Stream Stream, string V) {
            Write(Stream, Encoding.UTF8.GetBytes(V));
        }

        public static void Write(Stream Stream, byte[] B) {
            Stream.Write(B, 0, B.Length);
        }

        public static void Write(Stream Stream, byte[] B, int len) {
            Stream.Write(B, 0, len);
        }

        public static byte Read(Stream Stream) {
            byte[] B = new byte[1];
            Read(Stream, B);

            return B[0];
        }

        public static int Read(Stream Stream, byte[] B) {
            return Read(Stream, B, 0, B.Length, false);
        }

        public static int Read(Stream Stream, byte[] B, int len) {
            return Read(Stream, B, 0, len, false);
        }

        public static int Read(Stream Stream, byte[] B, int offset, int len) {
            return Read(Stream, B, offset, len, false);
        }

        static readonly int read_dummy_max = 3, ready_dummy_delay = 100;
        public static int Read(Stream Stream, byte[] B, int offset, int len, bool JustRead) {
            int r_total =
                Read(Stream, B, offset, len, read_dummy_max, ready_dummy_delay);

            if (!JustRead && r_total < len) {
                throw new CoreException(MethodBase.GetCurrentMethod(), "连续读取空值超限");
            }

            return r_total;
        }

        public static int Read(Stream Stream, byte[] B, int offset, int len, int times_try, int delay_each) {
            int r_len = 0, r_total = 0, dummy_counter = 0;
            while (r_total < len) {
                r_len =
                    Stream.Read(B, offset + r_total, len - r_total);

                if (r_len == 0) {
                    dummy_counter += 1;
                    if (dummy_counter > times_try) {
                        return r_total;
                    }

                    Thread.Sleep(delay_each);
                }
                r_total += r_len;
            }

            return r_total;
        }

        public static bool Read(Stream Stream, byte[] Buffer, int offset, int len_max, ref int len, byte[] end) {
            TreeMap map = new TreeMap();
            map.Create(end);
            int matchIndex = -1;
            len =
                Read(Stream, Buffer, offset, Math.Min(Buffer.Length - offset, len_max), ref matchIndex, map);

            return matchIndex >= 0;
        }

        public static int Read(Stream Stream, byte[] Buffer, ref int matchedIndex, TreeMap tree) {
            return Read(Stream, Buffer, 0, Buffer.Length, ref matchedIndex, tree);
        }

        public static int Read(Stream Stream, byte[] Buffer, int offset, ref int matchedIndex, TreeMap tree) {
            return Read(Stream, Buffer, offset, Buffer.Length - offset, ref matchedIndex, tree);
        }

        public static int Read(Stream Stream, byte[] Buffer, int offset, int len_max, ref int matchedIndex, TreeMap tree) {
            if(Buffer == null || Buffer.Length == 0) {
                throw new ArgumentNullException("Buffer");
            }
            if(tree == null) {
                throw new ArgumentNullException("tree");
            }
            if(offset < 0 || offset >= Buffer.Length) {
                throw new ArgumentOutOfRangeException("offset", "0->" + Buffer.Length.ToString());
            }
            if(len_max + offset > Buffer.Length) {
                throw new ArgumentOutOfRangeException("offset,len_max", (offset + len_max).ToString() + "->" + Buffer.Length.ToString());
            }

            matchedIndex = -1;

            int buffIndex = offset,
                max_index = len_max > 0 ? len_max + offset : Buffer.Length;

            TreeMap founder = tree;
            TreeMap last;// = founder.prev;
            do {
                Read(Stream, Buffer, buffIndex, 1);
                founder = founder.Find(Buffer[buffIndex]);
                buffIndex += 1;

                if(founder == null) {
                    founder = tree;
                    continue;
                }

                last = founder.prev;
                if(last == null) {
                    continue;
                }
                if(last.IsBoundary) {
                    matchedIndex = last.Index;
                    break;
                }
            } while(buffIndex < max_index);

            return buffIndex - offset;
        }
    }

    //B64
    public partial class Utility {
        #region B64

        static char[] _B64 =
            {'#','*','0','1','2','3','4','5','6','7','8','9',
            'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z',
            'a','b','c','d','e','f','g','h','i','j','k','l','m','n','o','p','q','r','s','t','u','v','w','x','y','z',
            };

        internal static string B64(byte[] B) {
            int vLength = B.Length / 3 * 4;
            int r = B.Length % 3;
            if (r > 0) {
                vLength += r + 1;
            }
            char[] V = new char[vLength];

            int bIndex = 0; int bCounter = 0; int bFinal = 0;
            int vIndex = 0; int vSum = 0;

            for (int iLength = 0; iLength < B.Length; iLength += 3) {
                vSum = 0;
                bCounter = 0; bFinal = iLength + 3;
                if (bFinal > B.Length) {
                    bFinal = B.Length;
                }
                for (; bIndex < B.Length && bIndex < bFinal; bIndex++, bCounter++) {
                    vSum += B[bIndex] << ((bCounter) << 3);
                }

                bFinal = bCounter + 1;
                for (int i = 0; i < bFinal; i++, vIndex++, vSum = vSum >> 6) {
                    V[vIndex] = _B64[vSum & 0x3F];
                }
            }

            return new string(V);
        }

        internal static byte[] B64(string V) {
            int bLength = V.Length / 4 * 3;
            int r = V.Length % 4 - 1;
            if (r > 0) {
                bLength += r;
            }

            byte[] B = new byte[bLength];

            int vIndex = 0; int vFinal = 0;
            int vCounter = 0; int vSum = 0;
            int bIndex = 0; int bFinal = 0;

            for (int Step = 0; Step < V.Length; Step += 4) {
                vCounter = 0; vSum = 0;
                vFinal = Step + 4;
                if (vFinal > V.Length) {
                    vFinal = V.Length;
                }
                for (; vIndex < vFinal; vIndex++, vCounter++) {
                    vSum +=
                        Array.BinarySearch<char>(_B64, V[vIndex]) << (6 * vCounter);
                }

                bFinal = bIndex + 3;
                if (bFinal > B.Length) {
                    bFinal = B.Length;
                }
                for (; bIndex < bFinal; bIndex++, vSum = vSum >> 8) {
                    B[bIndex] = (byte)(vSum & 0xFF);
                }
            }

            return B;
        }
        #endregion
    }

    //Net
    public partial class Utility {
        [Serializable]
        public class NetInterface {
            [XmlAttribute("Name")]
            public string Name { get; internal set; }
            [XmlAttribute("Description")]
            public string Description { get; internal set; }
            [XmlAttribute("MAC")]
            public string MAC { get; internal set; }
            [XmlElement("ID")]
            public string ID { get; internal set; }
            [XmlAttribute("IP")]
            public string IP { get; internal set; }
        }

        public class Net {
            public static TcpListener CheckAndCreateListener(string host, int port, int backlog = 0, bool IsExculsive = false) {
                IPEndPoint ipeLocal = new IPEndPoint(IPAddress.Parse(host), port);
                {//Check If Address Is Already In Use
                    IPEndPoint[] endPoints = IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpListeners();
                    for (int i = 0, len = endPoints.Length; i < len; i++) {
                        if (ipeLocal == endPoints[i]) {
                            throw new CoreException("Address Already In Use:[{0}]", ipeLocal);
                        }
                    }
                }

                TcpListener listener = new TcpListener(ipeLocal);
                listener.ExclusiveAddressUse = IsExculsive;
                if(backlog > 0) {
                    listener.Start(backlog);
                } else {
                    listener.Start();
                }

                return listener;
            }

            public static bool IsAvailable {
                get {
                    return NetworkInterface.GetIsNetworkAvailable();
                }
            }

            public static string HostName {
                get {
                    return Dns.GetHostName();
                }
            }

            public static NetInterface[] Interfaces {
                get {
                    return Net.GetInterfaces();
                }
            }

            static NetInterface[] GetInterfaces() {
                List<NetInterface> NIs = new List<NetInterface>();
                NetworkInterface[] NI = NetworkInterface.GetAllNetworkInterfaces();
                int lbIndex = NetworkInterface.LoopbackInterfaceIndex;

                for (int i = 0; i < NI.Length; i++) {
                    string MAC =
                        BitConverter.ToString(NI[i].GetPhysicalAddress().GetAddressBytes());
                    if (string.IsNullOrEmpty(MAC)) {
                        continue;
                    }

                    IPInterfaceProperties IPP = NI[i].GetIPProperties();
                    NetInterface ni = new NetInterface();
                    ni.Name = NI[i].Name;
                    ni.Description = NI[i].Description;
                    ni.MAC = MAC;
                    ni.ID = NI[i].Id;
                    ni.IP =
                        IPP.UnicastAddresses.Count > 0 ? IPP.UnicastAddresses[0].Address.ToString() : "";
                    NIs.Add(ni);
                }

                return NIs.ToArray();
            }
        }
    }
}
